/*******************************************************************************
 * Copyright (c) 2012, 2013 XML-Print Project.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     XML-Print Project - initial API and implementation
 ******************************************************************************/
package info.xmlprint.util;

import info.xmlprint.core.mapping.ElementPath;
import info.xmlprint.core.mapping.ElementPathNode;
import info.xmlprint.core.xml.PElement;
import info.xmlprint.ui.message.GuiMessages;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.eclipse.jface.dialogs.MessageDialog;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.XMLReader;

public class XmlUtil {

	public static Document creatNewDoc(String rootName) {
		Document doc = DocumentHelper.createDocument();
		doc.addElement(rootName);
		return doc;
	}

	public static String doc2IndentString(org.dom4j.Document doc) {
		Writer writer = null;
		try {
			OutputFormat of = new OutputFormat();
			of.setIndent(true);
			of.setNewlines(true);
			of.setEncoding("utf-8");
			of.setOmitEncoding(false);
			of.setExpandEmptyElements(false);
			writer = new StringWriter();
			XMLWriter xmlWriter = new XMLWriter(writer, of);
			xmlWriter.write(doc);
			xmlWriter.close();
		} catch (IOException e) {
			Logger.getLogger(FileUtil.class.getName()).error("", e);
			e.printStackTrace();
		}
		return writer.toString();
	}

	public static Document getDocByFile(String filePath) {
		File file = new File(filePath);

		SAXReader reader = new SAXReader();
		Document doc = null;
		try {
			doc = reader.read(file);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return doc;
	}

	public static Document getDocByStream(InputStream in) {
		if (in == null) {
			return null;
		}
		SAXReader reader = new SAXReader();
		Document doc = null;
		try {
			doc = reader.read(in);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return doc;
	}

	public static Document getDocByString(String str) {
		if (str == null) {
			return null;
		}

		SAXReader reader = new SAXReader();
		Document doc = null;
		try {
			doc = reader.read(str);
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return doc;
	}

	public static InputStream getStreamByDoc(Document doc) {

		if (doc == null) {
			return null;
		}

		OutputFormat of = new OutputFormat();
		of.setIndent(true);
		of.setNewlines(true);
		of.setEncoding("utf-8");
		of.setOmitEncoding(false);
		of.setExpandEmptyElements(false);

		try {

			ByteArrayOutputStream output = new ByteArrayOutputStream();
			XMLWriter xmlWriter = new XMLWriter(output, of);
			xmlWriter.write(doc);
			xmlWriter.flush();
			xmlWriter.close();

			InputStream input = new ByteArrayInputStream(output.toByteArray());
			output.close();
			return input;

		} catch (IOException e) {
			Logger.getLogger(XmlUtil.class.getName()).error("", e);
			e.printStackTrace();
		}

		return null;

	}

	/*
	 * write xml document to file
	 */
	public static boolean save2file(Document document, String filePath) {
		if (document == null)
			return false;
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding("utf-8");
		format.setOmitEncoding(false);
		format.setExpandEmptyElements(false);
		File file = new File(filePath);

		XMLWriter writer = null;
		try {
			writer = new XMLWriter(new FileWriter(file), format);
			writer.write(document);
			writer.close();
			return true;
		} catch (IOException e) {
			Logger.getLogger(XmlUtil.class.getName()).error("", e);
			e.printStackTrace();
			return false;
		}
	}

	public static void setDtdIgnore(XMLReader xmlReader) {
		// DTD declaration ignorieren
		try {
			xmlReader
					.setFeature(
							"http://apache.org/xml/features/nonvalidating/load-external-dtd",
							false);
			xmlReader.setFeature("http://xml.org/sax/features/validation",
					false);
		} catch (SAXNotRecognizedException e) {
			Logger.getLogger(FileUtil.class.getName()).error("", e);
			e.printStackTrace();
		} catch (SAXNotSupportedException e) {
			Logger.getLogger(FileUtil.class.getName()).error("", e);
			e.printStackTrace();
		}

	}

	public static boolean validateXMLUseDom4j(String text) {
		SAXReader saxReader = new SAXReader();
		Document document;
		try {
			document = saxReader.read(
					new ByteArrayInputStream(text.getBytes("UTF-8")), "UTF-8");
			document.getRootElement();
			return true;
		} catch (Exception e) {

			e.printStackTrace();
			return false;
		}
	}

	public static String xPathAddPrefix(String inputStr, String prefix) {
		if (prefix == null || prefix.equals("") || inputStr == null
				|| inputStr.equals(""))
			return inputStr;

		if (!inputStr.matches(".*/.*"))
			return prefix + ":" + inputStr;

		String[] xp_arr = inputStr.split("/");

		inputStr = "";

		int i = 0;
		for (String s : xp_arr) {
			if (i == 0) {
				i++;
				continue;
			}
			if (s == null)
				s = "";

			if (!s.equals("")) {
				// wenn mit prefix
				if (!s.matches("[^\\[]*:.*")) {
					s = prefix + ":" + s;
				}
			}

			inputStr += "/" + s;
		}
		return inputStr;
	}

	public static ElementPath string2ElementPath(String s, String indexStr,
			String mainNamespacePrefix) {
		int currNodeIndex = Integer.parseInt(indexStr);

		s = s.replaceAll("//", "/");

		String[] nodeStr = s.split("/");
		ElementPathNode parent = null;
		ElementPath ep = new ElementPath();
		int i = 0;
		for (String str : nodeStr) {
			if (str.equals(""))
				continue;

			// prefix entfernen
			str = str.replaceAll(mainNamespacePrefix + ":", "");

			if (str.matches("\\w*:\\w*")) {
				String err = GuiMessages.Stileditor_Error_Unknown_NamespacePrefix
						+ ": " + str;
				MessageDialog.openInformation(null, "", err);
			}

			// String ohne Attribute-Werte
			ElementPathNode epn = new ElementPathNode(new PElement(str
					.replaceAll("\\[.*\\]", "").trim()));

			// Attribut Werte definieren
			setAttrVal(epn, str);

			if (currNodeIndex == i)
				ep.setcurrPathNode(epn);

			ep.addNode(parent, epn);

			parent = epn;

			i++;
		}
		return ep;
	}

	private static void setAttrVal(ElementPathNode epn, String epStr) {
		Pattern p = Pattern.compile("\\[.*\\]");
		Matcher m = p.matcher(epStr);

		while (m.find()) {
			String attrValStr = epStr.substring(m.start() + 1, m.end() - 1);

			// Bsp:<ePath>//TEI:hi[@rend='bold' or @rend='italic']</ePath>
			// TODO: Muss noch getestet und verbesser werden
			String[] attrValArr = attrValStr.split(" or @");
			for (String str : attrValArr) {
				str = str.trim();
				str = str.replace("@", "");
				str = str.replace("\'", "");
				String[] strArr = str.split("=");
				if (strArr.length == 2) {
					epn.addValue(strArr[0].trim(), strArr[1].trim());
				}else{
					epn.addAttribut(strArr[0]);
					epn.addAttrWithSelectAll(strArr[0]);
				}
			}
		}
	}

	public static Document transform1(Source src, Document xslDoc) {
		if (xslDoc == null || src == null) {
			return null;
		}
		TransformerFactory transformerFactory = new net.sf.saxon.TransformerFactoryImpl();
		Document outDoc = null;
		try {

			Transformer xformer = transformerFactory
					.newTransformer(new StreamSource(new ByteArrayInputStream(
							xslDoc.asXML().getBytes("utf-8"))));

			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			xformer.transform(src, new StreamResult(bos));
			InputStream in = new ByteArrayInputStream(bos.toByteArray());
			outDoc = getDocByStream(in);
			in.close();
			bos.close();

		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return outDoc;
	}

	public static Document createBaseInnerLinkXsl() {
		Document doc = DocumentHelper.createDocument();
		Element root = doc.addElement("xsl:stylesheet");
		root.addAttribute("version", "2.0");
		root.addNamespace("xsl", "http://www.w3.org/1999/XSL/Transform");
		Element output = root.addElement("xsl:output");
		output.addAttribute("method", "xml");
		output.addAttribute("version", "1.0");
		output.addAttribute("indent", "no");
		output.addAttribute("encoding", "utf-8");
		Element template = root.addElement("xsl:template");
		template.addAttribute("match", "/ | @* | node()");
		Element copy = template.addElement("xsl:copy");
		copy.addElement("xsl:apply-templates").addAttribute("select",
				"@* | node()");
		return doc;
	}
}
